#pragma once

#include "Object.h"

Object::Object() {
	distanceToCamera = 0;
	D3DXMatrixIdentity(&transMatrix);
	D3DXMatrixIdentity(&scaleMatrix);
	D3DXMatrixIdentity(&rotateMatrix);
	D3DXMatrixIdentity(&rotateMatrixX);
	D3DXMatrixIdentity(&rotateMatrixY);
	D3DXMatrixIdentity(&rotateMatrixZ);
	updateMesh();

	MeshSysMem = NULL;
}

Object::Object(string mFile, string dFile, string iconLoc, string n, string a, D3DXMATRIX tMatrix)
:modelFile(mFile), dataFile(dFile), iconLocation(iconLoc), name(n), alignment(a), transMatrix(tMatrix) {
	distanceToCamera = 0;
	D3DXMatrixIdentity(&scaleMatrix);
	D3DXMatrixIdentity(&rotateMatrix);
	D3DXMatrixIdentity(&rotateMatrixX);
	D3DXMatrixIdentity(&rotateMatrixY);
	D3DXMatrixIdentity(&rotateMatrixZ);
	updateMesh();

	MeshSysMem = NULL;
}

Object::~Object(void) {
	//delete MeshMaterials;
	//delete MeshTextures;
}

void Object::deallocateMemory(void) {
	delete MeshMaterials;
	delete MeshTextures;
}

bool Object::initializeObject(LPDIRECT3DDEVICE9 &pDevice) {
	HRESULT hr;
	LPD3DXBUFFER ppAdjacencyBuffer;
	LPD3DXBUFFER pD3DXMtrlBuffer = NULL;

    hr = D3DXLoadMeshFromX(modelFile.c_str(), D3DXMESH_SYSTEMMEM, pDevice, 
                            &ppAdjacencyBuffer, &pD3DXMtrlBuffer, NULL,
                            &NumMaterials, &MeshSysMem);
    if(FAILED(hr))
		return false;

	D3DXMATERIAL* matMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
	MeshMaterials = new D3DMATERIAL9[NumMaterials];
	MeshTextures  = new LPDIRECT3DTEXTURE9[NumMaterials];
    
	for(DWORD i = 0; i < NumMaterials; i++) {
		MeshMaterials[i] = matMaterials[i].MatD3D;
		MeshMaterials[i].Ambient = MeshMaterials[i].Diffuse;

		// Make sure the texture name is valid
		if(matMaterials[i].pTextureFilename != NULL) {
			// Load the texture
			string tempLocation = "Graphics\\Textures\\";
			tempLocation.append(matMaterials[i].pTextureFilename);
			if(FAILED(D3DXCreateTextureFromFile(pDevice, tempLocation.c_str(), &MeshTextures[i]))) {
				_cprintf("Error! Could not load texture.\n");
				return false;
			}
		} else { // If no texture name, then NULL out this spot in the list
			MeshTextures[i] = NULL;
		}
	}

	//D3DXComputeTangentFrameEx(MeshSysMem,
	//						D3DDECLUSAGE_TEXCOORD,
	//						0/*TexStageIndex*/,
	//						D3DX_DEFAULT/*D3DDECLUSAGE_BINORMAL*/, 
	//						0,/*BinormIndex,*/ 
	//						D3DX_DEFAULT/*D3DDECLUSAGE_TANGENT*/,
	//						0,/*TangentIndex,*/
	//						D3DX_DEFAULT, // do not store normals
	//						0,
	//						D3DXTANGENT_WRAP_UV/*0*/ 
	//						| D3DXTANGENT_GENERATE_IN_PLACE 
	//						| D3DXTANGENT_ORTHOGONALIZE_FROM_U,
	//						0,/*pAdjacency,*/
	//						-1.01f, 
	//						-0.01f,
	//						-1.01f,
	//						NULL,
	//						NULL);


	// Release the material buffer
	if(pD3DXMtrlBuffer)
		pD3DXMtrlBuffer->Release();

	return true;
}

void Object::updateMesh(void) {
	meshMatrix = scaleMatrix * rotateMatrix;
	meshMatrix *= transMatrix;
}

void Object::drawMesh(LPDIRECT3DDEVICE9 &pDevice) {
	pDevice->SetTransform(D3DTS_WORLD, &meshMatrix);

	for(DWORD i = 0; i < NumMaterials; i++) {
		pDevice->SetMaterial(&MeshMaterials[i]);
		if(MeshTextures != NULL)
	        pDevice->SetTexture(0, MeshTextures[i]);
		MeshSysMem->DrawSubset(i);
	}
}

void Object::setObjectLocation(float x, float y, float z) {
	D3DXMatrixTranslation(&transMatrix, x, y, z);
	updateMesh();
}

void Object::moveObject(float x, float y, float z) {
	D3DXMatrixTranslation(&transMatrix, x + getPosition().x, y + getPosition().y, z + getPosition().z);
	updateMesh();
}

void Object::setObjectScale(float x, float y, float z) {
	D3DXMatrixScaling(&scaleMatrix, x, y, z);
	updateMesh();
}

void Object::scaleObject(float x, float y, float z) {
	D3DXMatrixScaling(&scaleMatrix, x + getScale().x, y + getScale().y, z + getScale().z);
	updateMesh();
}

void Object::rotateObject(float x, float y, float z) {
	D3DXMatrixRotationX(&rotateMatrixX, degToRadA(x + radToDegA(acos(rotateMatrixX._22))));
	D3DXMatrixRotationY(&rotateMatrixY, degToRadA(y + radToDegA(acos(rotateMatrixY._11))));
	D3DXMatrixRotationZ(&rotateMatrixZ, degToRadA(z + radToDegA(acos(rotateMatrixZ._11))));
	rotateMatrix = rotateMatrixX * rotateMatrixY * rotateMatrixZ;
	updateMesh();
}

void Object::setMeshMatrix(D3DXMATRIX mMatrix) {
	meshMatrix = mMatrix;
}
void Object::setTransMatrix(D3DXMATRIX tMatrix) {
	transMatrix = tMatrix;
}
void Object::setScaleMatrix(D3DXMATRIX sMatrix) {
	scaleMatrix = sMatrix;
}
void Object::setRotateMatrix(D3DXMATRIX rMatrix) {
	rotateMatrix = rMatrix;
}
void Object::setRotateXMatrix(D3DXMATRIX rXMatrix) {
	rotateMatrixX = rXMatrix;
}
void Object::setRotateYMatrix(D3DXMATRIX rYMatrix) {
	rotateMatrixY = rYMatrix;
}
void Object::setRotateZMatrix(D3DXMATRIX rZMatrix) {
	rotateMatrixZ = rZMatrix;
}
void Object::setAllMatrix(D3DXMATRIX mMatrix, D3DXMATRIX tMatrix, D3DXMATRIX sMatrix, D3DXMATRIX rMatrix, 
						  D3DXMATRIX rXMatrix, D3DXMATRIX rYMatrix, D3DXMATRIX rZMatrix) {
	setMeshMatrix(mMatrix);
	setTransMatrix(tMatrix);
	setScaleMatrix(sMatrix);
	setRotateMatrix(rMatrix);
	setRotateXMatrix(rXMatrix);
	setRotateYMatrix(rYMatrix);
	setRotateZMatrix(rZMatrix);
}

void Object::setMaterials(D3DMATERIAL9* MMaterials, DWORD numMaterials, LPD3DXMESH MSysMem) {
	MeshMaterials = MMaterials;
	NumMaterials = numMaterials;
	MeshSysMem = MSysMem;
}

void Object::setAlignment(string a) {
	alignment = a;
}

void Object::setDistToCam(float dist) {
	distanceToCamera = dist;
}

D3DXMATRIX Object::getMMatrix(void) {
	return meshMatrix;
}

D3DXMATRIX Object::getTMatrix(void) {
	return transMatrix;
}

D3DXMATRIX Object::getSMatrix(void) {
	return scaleMatrix;
}

D3DXMATRIX Object::getRMatrix(void) {
	return rotateMatrix;
}

D3DXMATRIX Object::getRXMatrix(void) {
	return rotateMatrixX;
}

D3DXMATRIX Object::getRYMatrix(void) {
	return rotateMatrixY;
}

D3DXMATRIX Object::getRZMatrix(void) {
	return rotateMatrixZ;
}

D3DMATERIAL9* Object::getMeshMaterials(void) {
	return MeshMaterials;
}
DWORD Object::getNumMaterials(void) {
	return NumMaterials;
}
LPD3DXMESH Object::getMeshSysMem(void) {
	return MeshSysMem;
}

string Object::getModelFile(void) {
	return modelFile;
}

string Object::getDataFile(void) {
	return dataFile;
}

string Object::getIconLocation(void) {
	return iconLocation;
}

string Object::getName(void) {
	return name;
}

string Object::getAlignment(void) {
	return alignment;
}

float Object::getDistToCam(void) {
	return distanceToCamera;
}

XYZ Object::getPosition(void) {
	XYZ temp = {transMatrix._41, transMatrix._42, transMatrix._43};
	return temp;
}
XYZ Object::getScale(void) {
	XYZ temp = {scaleMatrix._11, scaleMatrix._22, scaleMatrix._33};
	return temp;
}
XYZ Object::getRotation(void) {
	XYZ temp = {radToDegA(acos(rotateMatrixX._22)), 
				radToDegA(acos(rotateMatrixY._11)), 
				radToDegA(acos(rotateMatrixZ._11))
	};
	return temp;
}

void Object::saveInfoToFile(ofstream &out) {
	out << modelFile << "\n";
	out << dataFile << "\n";
	out << iconLocation << "\n";
	out << name << "\n";
	out << alignment << "\n";
	out << getPosition().x << " ";
	out << getPosition().y << " ";
	out << getPosition().z << "\n";
	out << getScale().x << " ";
	out << getScale().y << " ";
	out << getScale().z << "\n";
	out << getRotation().x << " ";
	out << getRotation().y << " ";
	out << getRotation().z << "\n";
}